﻿namespace Sitecore.ChangesTracker.Events
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using Data;
  using Data.Archiving;
  using Data.Items;
  using Diagnostics;
  using Sitecore.ChangesTracker.Archiving;
  using Sitecore.Data.Engines;
  using Sitecore.Data.Events;
  using Sitecore.Data.Managers;
  using Sitecore.Events;
  using Sitecore.ChangesTracker.Configuration;
  using Sitecore.Data.Templates;

  public class LimitedDictionary : Dictionary<string, DateTime>
  {
    public void Add(string key, DateTime value)
    {
      if (this.ContainsKey(key)||this.Count >= 100)
      {
        CleanUp();
      }
      if (this.ContainsKey(key))
      {
        return;
      }
      base.Add(key+"_"+System.Threading.Thread.CurrentThread.ManagedThreadId.ToString(), value);
    }
    void CleanUp()
    {
      for (int i = 0; i < this.Count; ++i)
      {
        if (DateTime.Now - this[this.Keys.ElementAt(i) ] > TimeSpan.FromSeconds(Settings.EventTimeout))
        {
          this.Remove(this.Keys.ElementAt(i));
        }
      }
    }
    public bool ContainsKey(string key)
    {
      this.CleanUp();
      return base.ContainsKey(key + "_" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
    }
  }

  public class TemplateTracker 
  {
    class Track
    {
      public DateTime Stamp { get; set; }
      public bool Processed { get; set; }
    }
    static Dictionary<ID, Track> templates = new Dictionary<ID, Track>();
    public static void TrackTemplate(Template templ)
    {
      Add(templ);
    }

    private static void Add(Template templ)
    {
      CleanUp();
      if (!templates.ContainsKey(templ.ID))
      {
        Track t = new Track();
        t.Stamp = DateTime.Now;
        t.Processed = false;
        templates.Add(templ.ID, t);
      }
    }

      public static bool IsTemplateTracked(ID id)
      {
          CleanUp();
          return templates.ContainsKey(id);
      }

      static void CleanUp()
    {
      for (int i = 0; i < templates.Count; ++i)
      {
        KeyValuePair<ID, Track> tr=templates.ElementAt(i);
        if (DateTime.Now - tr.Value.Stamp > TimeSpan.FromSeconds(Settings.EventTimeout))
        {
          templates.Remove(tr.Key);
        }
      }
    }

    public static void TrackTemplate(Item templ)
    {
      TrackTemplate(TemplateManager.GetTemplate(templ.ID, templ.Database));
    }

    public static Item GetItemToArchive(Item item, string info)
    {
      if (!TemplateManager.IsTemplate(item) && !TemplateManager.IsTemplatePart(item))
      {
        return item;
      }
      Item template = FindTemplate(item);
      Template t = TemplateManager.GetTemplate(template.ID, template.Database);
        if (t != null)
        {
            TrackerManager.TemplateHashCache.ClearForTemplate(t);
        }
        ID id = template.ID;
        bool result = templates.ContainsKey(id);      
      if (result)
      {
        //templates[id].Count--;
        if (!templates[id].Processed)
        {
            if (info == "created")
            {
                item = null;
            }
            else
            {
                item = template;
                templates[id].Processed = true;
            }
        }
        else
        {
          item = null;
        }
        //if (templates[id].Count <= 0)
        //{
        //  templates.Remove(id);
        //}
      }
      return item;
    }

    public static Item FindTemplate(Item templatePart)
    {
      Item template = templatePart;
      while (!TemplateManager.IsTemplate(template))
      {
        template = template.Parent;
      }
      return template;
    }
  }

  public class ItemEventHandler
  {
      //public static LimitedDictionary itemsToSkip = new LimitedDictionary();

      public void OnItemCreated(object sender, EventArgs args)
      {
          if (args != null)
          {
              ItemCreatedEventArgs a = Event.ExtractParameter(args, 0) as ItemCreatedEventArgs;
              Assert.IsNotNull(a, "No data in parameters");
              //this.ArchiveItem(a.Item, "created");
              //itemsToSkip.Add(a.Item.ID.ToString(), DateTime.Now);
              TrackerManager.ArchiveItem(a.Item, "created");
              TrackerManager.SkipItem(a.Item);
              
          }
      }     

      public virtual void OnItemCreating(object sender, EventArgs args)
      {
          if (args != null)
          {
              ItemCreatingEventArgs a = Event.ExtractParameter(args, 0) as ItemCreatingEventArgs;
              Assert.IsNotNull(a, "No data in parameters");
              if (TemplateManager.IsTemplate(a.Parent))
              {
                  TrackerManager.ArchiveItem(a.Parent,"changed");
                  TrackerManager.SkipItem(a.ItemId.ToString());
              }
              
          }
      }

      protected virtual void OnItemSaving(object sender, EventArgs args)
      {
          if (args != null)
          {
              Item item = Event.ExtractParameter(args, 0) as Item;
              Assert.IsNotNull(item, "No item in parameters");
              //this.ArchiveItem(item, "changed");

              ItemChanges chamnges = (ItemChanges)item.GetType().GetMethod("GetChanges", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).Invoke(item, new object[]{true});
              string reason = chamnges.Renamed&&!TemplateManager.IsTemplate(item)&&!TemplateManager.IsTemplatePart(item)? "renamed" : "changed";
              TrackerManager.ArchiveItem(item, reason);
          }
      }

      protected virtual void OnVersionAdding(object sender, EventArgs args)
      {
          if (args != null)
          {
              Item item = Event.ExtractParameter(args, 0) as Item;
              Assert.IsNotNull(item, "No item in parameters");
              //itemsToSkip.Add(item.ID.ToString(), DateTime.Now);
              TrackerManager.SkipItem(item);
          }
      }

      protected virtual void OnItemDeleting(object sender, EventArgs args)
      {
          if (args != null)
          {
              Item item = Event.ExtractParameter(args, 0) as Item;
              Assert.IsNotNull(item, "No item in parameters");
              string info = "deleted";
              Item itemToArchive = item;
              if (TemplateManager.IsTemplatePart(item))
              {
                  itemToArchive = TemplateTracker.FindTemplate(item);
                  TemplateTracker.TrackTemplate(itemToArchive);
                  info = "changed";
              }
              if (TemplateManager.IsTemplate(item)&&TemplateTracker.IsTemplateTracked(item.ID))
              {
                  info = "deleted";
              }
              //this.ArchiveItem(item, "deleted");
              TrackerManager.ArchiveItem(itemToArchive, info);
          }
      }

      protected virtual void OnItemMoving(object sender, EventArgs args)
      {
          if (args != null)
          {
              Item item = Event.ExtractParameter(args, 0) as Item;
              Assert.IsNotNull(item, "No item in parameters");
              //this.ArchiveItem(item, "moved");
              //itemsToSkip.Add(item.ID.ToString(), DateTime.Now);
              TrackerManager.ArchiveItem(item, "moved");
              TrackerManager.SkipItem(item);
          }
      }

      protected virtual void OnItemVersionRemoving(object sender, EventArgs args)
      {
          if (args != null)
          {
              Item item = Event.ExtractParameter(args, 0) as Item;
              Assert.IsNotNull(item, "No item in parameters");
              //this.ArchiveItem(item, "version removed");
              TrackerManager.ArchiveItem(item, "version removed");
          }
      }

      //public void ArchiveItem(Item item, string info,out Guid archivalId)
      //{
      //    archivalId = Guid.Empty;
      //    if (this.ShouldBeArchived(item))
      //    {
      //        item = TemplateTracker.GetItemToArchive(item);
      //        if (item != null)
      //        {
      //            this.DoArchive(item, info,out archivalId);
      //        }
      //    }
      //}

      //public void ArchiveItem(Item item, string info)
      //{
      //    Guid archivalId = Guid.Empty;
      //    if (this.ShouldBeArchived(item))
      //    {
      //        item = TemplateTracker.GetItemToArchive(item);
      //        if (item != null)
      //        {
      //            this.DoArchive(item, info, out archivalId);
      //        }
      //    }
      //}

      //private void DoArchive(Item item, string info, out Guid archivalId)
      //{
      //    Database database = item.Database;
      //    archivalId = Guid.Empty;
      //    Assert.IsNotNull(database, typeof(Database), "Name: {0}", new object[]
      //  {
      //    item.Database.Name
      //  });

      //    ChangeTrackerArchive archive = ArchiveManager.Providers["changetracker"].GetArchive("changetracker", database) as ChangeTrackerArchive;
      //    if (archive != null)
      //    {
      //        Log.Audit(this, "Archive item: {0}", new string[]
      //    {
      //      AuditFormatter.FormatItem(item)
      //    });

      //        archivalId= archive.ArchiveItem(item, info);
      //    }
      //}

      //protected virtual bool ShouldBeArchived(Item item)
      //{
      //    if (ArchiveDisabler.IsActiveForItem(item.ID.ToGuid()))
      //    {
      //        return false;
      //    }
      //    return !itemsToSkip.ContainsKey(item.ID.ToString());
      //}

  }
}

